#pragma once

#include <string>
#include <iostream>
#include <vector>
#include <pthread.h>
#include <queue>
#include "Thread.hpp"
#include "Task.hpp"
#include <unistd.h>
#include "Log.hpp"


using namespace ThreadModle;
using namespace log_ns;


static const int gdefaultnum = 5;
void test()
{
    while (true)
    {
        std::cout << "hello linux" << std::endl;
        sleep(1);
    }
}

template <class T>
class ThreadPool
{
private:
    // 锁住任务队列(保护任务队列)
    void LockTaskQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void UnLockTaskQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool isEmpty()
    {
        return _task_queue.empty();
    }
    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }
    // 唤醒所有的线程，方便让线程退出
    void WakeUpAll()
    {
        pthread_cond_broadcast(&_cond);
    }

    void Sleep()
    {
        pthread_cond_wait(&_cond, &_mutex); // 休眠时需要释放掉锁
    }

    // 处理任务，注意在传这个函数指针的时候要注意，他内部有隐含有this指针
    void HandlerTask(const std::string &name)
    {
        while (true)
        {
            // 取任务
            LockTaskQueue();
            // 访问临界资源
            // 如果当前没有任务，并且线程池并不退出正在运行，才让线程休眠
            while (isEmpty() && _isrunning) // 如果任务队列为空，线程就能休眠了，防止伪唤醒的情况这里使用while,不满足情况就一直wait，再判断
            {
                _sleep_thread_num++; // 任务为空，将休眠线程+1
                // std::cout << name << " quit" << std::endl;
                LOG(INFO, "%s thread sleep...\n", name.c_str());

                Sleep();
                LOG(INFO, "%s thread wakeup...\n", name.c_str());

                _sleep_thread_num--;
            }
            // 判定一种情况:任务队列为空了，并且运行状态为非运行
            if (isEmpty() && !_isrunning)
            {

                // 直接解锁任务队列;
                UnLockTaskQueue();
                LOG(INFO, "%s thread quit...\n", name.c_str());
                // 直接退出循环：
                break;
            }
            // 有任务 || 被唤醒，在这里一定是有任务才被唤醒
            T t = _task_queue.front();
            _task_queue.pop();
            UnLockTaskQueue();
            // 处理任务，此处不能/也不能在临界区
            // 因为这个任务一旦被取出来了，就从队列当中被移走了，放在一个临时空间里
            t();
            // std::cout << name << " : " << t.result() << std::endl;
            LOG(DEBUG, "handler task done, task is : %s\n", t.result().c_str());

        }
    }

public:
    // 实际上对于线程池需要外部传的就是线程的数量
    ThreadPool(int thread_num = gdefaultnum)
        : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0) // 最开始没有线程在休眠
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    // 初始化线程池
    void Init()
    {
        // 构造一个function线程池类来构造的对象
        func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1); // 直接把this传过去(改变参数数量)
        for (int i = 0; i < _thread_num; i++)
        {
            std::string threadname = "thread-" + std::to_string(i + 1);
            _threads.emplace_back(threadname, func);
            LOG(DEBUG, "construct thread %s done, init success\n", threadname.c_str());
        }
    }

    // 让线程池中的线程工作
    void Start()
    {
        _isrunning = true;
        for (auto &thread : _threads)
        {
            thread.Start();
            LOG(DEBUG, "Start thread %s done, init success\n", thread.Name().c_str());
        }
    }

    // 让线程池中线程停止工作
    void Stop()
    {
        // 保证他的安全性，带锁
        LockTaskQueue();

        _isrunning = false;
        // 仅仅设为false，有可能所有的线程都在休眠，线程无法被唤醒，因此线程无法退出
        // 因此需要唤醒所有的线程
        WakeUpAll();
        UnLockTaskQueue();
        LOG(INFO, "Thread Pool Stop success\n");
    }

    // 像线程池推送任务
    void Equeue(const T &in)
    {
        LockTaskQueue();
        //线程池必须处于运行状态才能push任务
        if (_isrunning)
        {
            _task_queue.push(in);
            // 唤醒线程，是否有正在休眠的线程_sleep_thread_num
            if (_sleep_thread_num > 0) // 只要有线程在休眠，就唤醒
                WakeUp();              // 没有休眠的线程，就会在这等待
        }
        UnLockTaskQueue();
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    // 1.期望的线程数量
    int _thread_num;

    // 2.线程队列
    std::vector<Thread> _threads;

    // 3.任务队列 --- 共享资源 --- 需要被保护
    std::queue<T> _task_queue;

    // 4.线程池是否正在运行
    bool _isrunning;

    // 因为需要线程访问任务队列，因此访问任务队列的代码就是临界资源
    // 5.加锁保护队列，为什么不用信号量，因为当前队列只能整体使用，
    pthread_mutex_t _mutex;

    // 6.条件变量
    pthread_cond_t _cond;

    // 7.计数器，休眠线程的数量
    int _sleep_thread_num;
};